using Joy.Erp.BasicArchives.Core;
using Joy.Erp.BasicArchives.Core.Events;
using Joy.Erp.BasicArchives.Core.UniqueCodes;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Domain.Services;
using Volo.Abp.EventBus.Distributed;
using Volo.Abp.Settings;
using Volo.Abp.Uow;
using Volo.Abp.Users;

namespace Joy.Erp.BasicArchives.Production
{
    public partial class ProductionOrderManager : DomainService
    {
        protected ICurrentUser CurrentUser => LazyServiceProvider.LazyGetRequiredService<ICurrentUser>();
        protected IProductionOrderRepository ProductionOrderRepository => LazyServiceProvider.LazyGetRequiredService<IProductionOrderRepository>();
        protected IDistributedEventBus DistributedEventBus => LazyServiceProvider.LazyGetRequiredService<IDistributedEventBus>();
        protected ISettingProvider SettingProvider => LazyServiceProvider.LazyGetRequiredService<ISettingProvider>();
        protected IUniqueCodeSetter UniqueCodeSetter => LazyServiceProvider.LazyGetRequiredService<IUniqueCodeSetter>();

        [UnitOfWork]
        public async Task<ProductionOrder> CreateAsync(ProductionOrder entity, bool autoSave = false, CancellationToken cancellationToken = default)
        {
            entity.SetId(GuidGenerator.Create());
            entity.TenantId = CurrentTenant.Id;

            foreach (var child in entity.Children)
            {
                child.SetId(GuidGenerator.Create());
                child.DocumentId = entity.Id;
            }

            if (entity.Code.IsNullOrWhiteSpace())
            {
                var uniqueCodeConfigration = await SettingProvider.GetOrNullAsync($"UniqueCodeConfigration:{typeof(ProductionOrder).FullName}");
                UniqueCodeSetter.SetCode(entity, uniqueCodeConfigration);
            }

            await DistributedEventBus.PublishAsync(new EntityCreatingEto<ProductionOrder>(entity), false);
            return await ProductionOrderRepository.InsertAsync(entity, autoSave, cancellationToken);
        }

        [UnitOfWork]
        public async Task<ProductionOrder> UpdateAsync(ProductionOrder entity, bool autoSave = false, CancellationToken cancellationToken = default)
        {
            foreach (var child in entity.Children)
            {
                if (child.Id == Guid.Empty) child.SetId(GuidGenerator.Create());
                child.DocumentId = entity.Id;
            }

            await DistributedEventBus.PublishAsync(new EntityUpdatingEto<ProductionOrder>(entity), false);

            return await ProductionOrderRepository.UpdateAsync(entity, autoSave, cancellationToken);
        }


        [UnitOfWork]
        public async Task<ProductionOrder> ChangeStatus(Guid id, DocumentStatus documentStatus, bool autoSave = false, CancellationToken cancellationToken = default)
        {
            var entity = await ProductionOrderRepository.GetAsync(id, true);
            entity.DocumentStatus = documentStatus;

            await DistributedEventBus.PublishAsync(new EntityStatusChangingEto<ProductionOrder>(entity, documentStatus), false);

            return await ProductionOrderRepository.UpdateAsync(entity, autoSave, cancellationToken);
        }

        [UnitOfWork]
        public async Task DeleteAsync(Guid id, bool autoSave = false, CancellationToken cancellationToken = default)
        {
            var entity = await ProductionOrderRepository.GetAsync(id, true);

            await ProductionOrderRepository.EnsureCollectionLoadedAsync(entity, x => x.Children, cancellationToken);
            entity.Children.Clear();

            await DistributedEventBus.PublishAsync(new EntityDeletingEto<ProductionOrder>(entity), false);
            await ProductionOrderRepository.DeleteAsync(entity, autoSave, cancellationToken);
        }

        [UnitOfWork]
        public async Task<List<ProductionOrder>> CreateManyAsync(IEnumerable<ProductionOrder> entities, bool useBulk = false, bool autoSave = false, CancellationToken cancellationToken = default)
        {
            var entityList = entities?.Where(x => x != null).ToList() ?? new List<ProductionOrder>();
            if (entityList.Count == 0)
            {
                return entityList;
            }

            var uniqueCodeConfigration = await SettingProvider.GetOrNullAsync($"UniqueCodeConfigration:{typeof(ProductionOrder).FullName}");

            foreach (var entity in entityList)
            {
                entity.SetId(GuidGenerator.Create());
                entity.TenantId = CurrentTenant.Id;

                if (useBulk)
                {
                    entity.CreationTime = Clock.Now;
                    entity.CreatorId = CurrentUser.Id;
                }

                if (entity.Children != null)
                {
                    foreach (var child in entity.Children)
                    {
                        child.SetId(GuidGenerator.Create());
                        child.DocumentId = entity.Id;

                        if (useBulk)
                        {
                            child.CreationTime = Clock.Now;
                            child.CreatorId = CurrentUser.Id;
                        }
                    }
                }

                if (entity.Code.IsNullOrWhiteSpace())
                {
                    UniqueCodeSetter.SetCode(entity, uniqueCodeConfigration);
                }

                await DistributedEventBus.PublishAsync(new EntityCreatingEto<ProductionOrder>(entity), false);
            }

            if (useBulk)
            {
                await ProductionOrderRepository.BulkInsertAsync(entityList, autoSave, cancellationToken);
            }
            else
            {
                await ProductionOrderRepository.InsertManyAsync(entityList, autoSave, cancellationToken);
            }

            return entityList;
        }

        [UnitOfWork]
        public async Task<List<ProductionOrder>> UpdateManyAsync(IEnumerable<ProductionOrder> entities, bool useBulk = false, bool autoSave = false, CancellationToken cancellationToken = default)
        {
            var entityList = entities?.ToList() ?? new List<ProductionOrder>();
            if (entityList.Count == 0) return entityList;

            foreach (var entity in entityList)
            {
                if (entity.Children != null)
                {
                    foreach (var child in entity.Children)
                    {
                        if (child.Id == Guid.Empty)
                        {
                            child.SetId(GuidGenerator.Create());

                            if (useBulk)
                            {
                                child.CreationTime = Clock.Now;
                                child.CreatorId = CurrentUser.Id;
                            }
                        }
                        child.DocumentId = entity.Id;
                    }
                }

                await DistributedEventBus.PublishAsync(new EntityUpdatingEto<ProductionOrder>(entity), false);
            }

            if (useBulk)
            {
                await ProductionOrderRepository.BulkUpdateAsync(entityList, autoSave, cancellationToken);
            }
            else
            {
                await ProductionOrderRepository.UpdateManyAsync(entityList, autoSave, cancellationToken);
            }

            return entityList;
        }

        [UnitOfWork]
        public async Task DeleteManyAsync(IEnumerable<ProductionOrder> entities, bool useBulk = false, bool autoSave = false, CancellationToken cancellationToken = default)
        {
            var entityList = entities?.Where(x => x != null).ToList() ?? new List<ProductionOrder>();
            if (entityList.Count == 0)
            {
                return;
            }

            foreach (var entity in entityList)
            {
                await ProductionOrderRepository.EnsureCollectionLoadedAsync(entity, x => x.Children, cancellationToken);
                entity.Children?.Clear();

                await DistributedEventBus.PublishAsync(new EntityDeletingEto<ProductionOrder>(entity), false);
            }

            if (useBulk)
            {
                await ProductionOrderRepository.BulkDeleteAsync(entityList, autoSave, cancellationToken);
            }
            else
            {
                foreach (var entity in entityList)
                {
                    await ProductionOrderRepository.DeleteAsync(entity, autoSave, cancellationToken);
                }
            }
        }
    }
}